#include "ed.h"


#define NOT_USE_PRINTF
#define ED_MAX_ACKS 16
#define ED_HEAP_SIZE 16
#define ED_MSG_NEIGH 0
#define ED_MSG_MODEL_DEF 1

#define ED_MSG_MODEL 100
//tag i, i from 100 to MAX_TAGS_TYPE-1+100 means a model message with type in position i-100 in the type vector
#define ED_MSG_TERM_ACK 200
#define ED_MSG_TERM_END 300

#define ED_MODEL_TYPESC 2




int rank, size, v1 = 0, msgc = 0, *destv, active = 0, terminatedGlobalTermination = 1, sender = 0, result = 0, resultGlobalTermination = 0, tag_in, tag_out, neighc = 0, parentGlobal = -1, globalTermination = 0, flush = 0, ackc_in = 0, ackc_out = 0, msgc_out = 0, type_msg_ord, m, i, fifo_out_order = 1, take_out_heap = 1, size_of_int, *type_msgv, type_index, termc=0, msg_end=0,  buffsize=0, sent_term=0, enabled_to_recv=0, j, k, matrixIsNull=0;

long int begin;

void *msgv, *msg;

MPI_Request request;
MPI_Status status;




typedef struct Node {
    int sender, tag, seq, type_index, ** MatrixCausal;
    void *msg;
	
} Node;

//send and receive primitives
int ED_Send(void *msg, int type_index, int dest, int tag_out, int *sendcountv, int MatrixCausal[][size]);
void *ED_Receive(int *sender, int *type_index, int *tag_in, int *recvcountv, int MatrixCausal[][size], Node heap[][ED_HEAP_SIZE], int *heap_count, int *heap_free);

//function to compare two nodes from heap
int ED_Compare_Node_fifo(const void *a, const void *b);
int ED_Compare_Node_causal(const void *a, const void *b);


int main(int argc, char *argv[])
{

    MPI_Init(&argc, &argv);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    int size_of_int = sizeof(int);
    typescount=ED_MODEL_TYPESC;
    size_elem_typev[0] = size_of_int;
    tag_typev[0] = MPI_INT;
    tag_typev[1] = MPI_PACKED;	
    int neighv[size], ackv[size], sendcountv[size], recvcountv[size], heap_count[size], heap_free[size], MatrixCausal[size][size];
    Node heap[size][ED_HEAP_SIZE];

    #ifndef NOT_USE_PRINTF 
	printf("MODEL MSG: I am process %d of %d\n", rank, size);
    #endif

    if (rank == 0) {
	FILE *pFile;
	int v2;
	pFile = fopen(argv[1], "r");
	if (pFile == NULL)
	    perror("SYSTEM MSG: I am process %d! Error opening graph file!\n");
	else {

	    fscanf(pFile, "%d", &m);
	    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: Graph has %d vertices and %d edges!\n", size, m);
#endif

	    for (i = 0; i < m; i++) {
		fscanf(pFile, "%d %d\n", &v1, &v2);
		#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: %d is neighbor of %d\n", v1, v2);
#endif


		if (v1 == 0 || v2 == 0) {
		    if (v1 == 0) {
			neighv[neighc++] = v2;

			MPI_Isend(&v1, 1, MPI_INT, v2, ED_MSG_NEIGH, MPI_COMM_WORLD, &request);

		    }
		    if (v2 == 0) {
			neighv[neighc++] = v1;
			MPI_Isend(&v2, 1, MPI_INT, v1, ED_MSG_NEIGH, MPI_COMM_WORLD, &request);

		    }

		} else {

		    MPI_Isend(&v1, 1, MPI_INT, v2, ED_MSG_NEIGH, MPI_COMM_WORLD, &request);
		    MPI_Isend(&v2, 1, MPI_INT, v1, ED_MSG_NEIGH, MPI_COMM_WORLD, &request);
		}

	    }
	    v1 = -1;

	    globalTermination = atoi(argv[2]);
	    type_msg_ord = atoi(argv[3]);

	    for (i = 1; i < size; i++) {

		MPI_Isend(&v1, 1, MPI_INT, i, ED_MSG_NEIGH, MPI_COMM_WORLD, &request);
// Nodededede 0 sends to all Nodedededes the characteristics of the model
//global termination

		MPI_Isend(&globalTermination, 1, MPI_INT, i, ED_MSG_MODEL_DEF, MPI_COMM_WORLD, &request);
		MPI_Isend(&type_msg_ord, 1, MPI_INT, i, ED_MSG_MODEL_DEF, MPI_COMM_WORLD, &request);


	    }
	}

	fclose(pFile);

    } else {

	while (v1 != -1) {
	    MPI_Recv(&v1, 1, MPI_INT, 0, ED_MSG_NEIGH, MPI_COMM_WORLD, &status);

	    if (v1 != -1)

		neighv[neighc++] = v1;


	}
	MPI_Recv(&globalTermination, 1, MPI_INT, 0, ED_MSG_MODEL_DEF, MPI_COMM_WORLD, &status);
	MPI_Recv(&type_msg_ord, 1, MPI_INT, 0, ED_MSG_MODEL_DEF, MPI_COMM_WORLD, &status);

    }

    if (globalTermination == 1) {
	terminatedGlobalTermination = 0;
    }


    for (i = 0; i < size; i++) {
	ackv[i] = 0;
	sendcountv[i] = 0;
	recvcountv[i] = 0;
	heap_count[i] = 0;
	heap_free[i] = 0;
	heap[i][0].seq=0;
	if(type_msg_ord==2){
	heap[i][0].MatrixCausal=NULL;
	for(j=0;j<size;j++){
		
			MatrixCausal[i][j]=0;

		}

	}
    }

    ED_Init(argc, argv, rank, neighc, neighv);


begin=clock();

    if (rank == 0) {

	active = ED_Event(NULL, 0, -1, &msgc, &destv, &msgv, &type_msgv);
	
	#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I've processed an EVENT. Active: %d. %d messages generated!\n", rank, active, msgc);
	#endif



	msgc_out = msgc;
	for (i = 0; i < msgc; i++) {
    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I'm sending message number %d to %d with type index %d!\n", rank, i, destv[i], type_msgv[i]);
	#endif	
	    ED_Send(msgv + i * size_elem_typev[type_msgv[i]], type_msgv[i], destv[i], ED_MSG_MODEL, sendcountv, MatrixCausal);

	}
	msgc = 0;
    }

        if(rank!=0 && globalTermination==0){
	active=1;
	}	

    while (((globalTermination==1) && (termc<neighc))  || ((globalTermination == 0) && (active == 1)) ) {
	

	if(rank!=0 && globalTermination==0){
	active=0;
	}	

	#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! Waiting for messages. Params => Termc: %d. Neighc: %d. Active: %d GlobalTermination %d!\n", rank, termc, neighc, active, globalTermination);
#endif

	msg = ED_Receive(&sender, &type_index, &tag_in, recvcountv, MatrixCausal, heap, heap_count, heap_free);

	
   	if (tag_in == ED_MSG_TERM_ACK) {

		msgc_out -= *(int *) msg;
		
#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I've received an ACK message %d from %d with tag %d. Deficit: %d!\n", rank, *(int *) msg, sender, tag_in, msgc_out);
	#endif
	
	} else

	 if (tag_in == ED_MSG_TERM_END) {

	termc++;
#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I've received an END message from %d. I have termc = %d!\n", rank, sender, termc);
	#endif


		if(sent_term==0){
	         sent_term=1;			
		 for (i = 0; i < neighc; i++) {

			#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I am sending END message %d of size %d to node %d with tag %d\n", rank, msg_end, 1, neighv[i], ED_MSG_TERM_END);
#endif
			
			ED_Send(&msg_end, 0, neighv[i], ED_MSG_TERM_END, sendcountv, MatrixCausal);
			
		    }
		  

		}
	

	}


	else {

	//computational message

	if(globalTermination == 1){
	if (parentGlobal == -1 && rank!=0 && active==0) {
		    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I'm entering the computation. My parent is %d!\n", rank, sender);
#endif
		    parentGlobal = sender;
		}
	else{
	
	ackv[sender]++;
	
	#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I'm storing ack to %d. %d acks to him!\n", rank, sender, ackv[sender]);


#endif
	}
	

	}
	//active = 0;
	    active  = ED_Event(msg, type_index, sender, &msgc, &destv, &msgv, &type_msgv);

	#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I've processed an EVENT. Active: %d. %d messages generated!\n", rank, active, msgc);
	#endif
	    msgc_out += msgc;
	
	for (i = 0; i < msgc; i++) {
	    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I'm sending message number %d to %d with type index %d!\n", rank, i, destv[i], type_msgv[i]);
	#endif	
    ED_Send(msgv + i * size_elem_typev[type_msgv[i]], type_msgv[i], destv[i], ED_MSG_MODEL, sendcountv, MatrixCausal);
	}
	msgc = 0;


	if(globalTermination ==1 && ackv[sender]>=ED_MAX_ACKS){

	#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I am sending ACK message %d of size %d to node %d with tag %d\n", rank, ackv[sender], 1, sender, ED_MSG_TERM_ACK);
#endif
			ED_Send(&ackv[sender], 0, sender, ED_MSG_TERM_ACK, sendcountv, MatrixCausal);
			ackv[sender] = 0;
				
	}


	}

	if(globalTermination ==1 && msgc_out == 0 && active == 0){

		
		    #ifndef NOT_USE_PRINTF
 printf("MODEL MSG: I am %d and I'm gonna flush ack messages\n", rank);
#endif
		    for (i = 0; i < size; i++) {
			if (ackv[i] > 0 && i != rank ) {
			    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I am sending ACK FLUSH message %d of size %d to node %d with tag %d\n", rank, ackv[i], 1, i, ED_MSG_TERM_ACK);
#endif
			    ED_Send(&ackv[i], 0, i, ED_MSG_TERM_ACK, sendcountv, MatrixCausal);
			    ackv[i] = 0;
			}
		    }
		


	if (parentGlobal!=-1){
	
	
	ackv[parentGlobal]++;
		#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I am sending ACK message %d TO PARENT %d of size %d with tag %d\n", rank, ackv[parentGlobal], parentGlobal, 1, ED_MSG_TERM_ACK);
#endif
			ED_Send(&ackv[parentGlobal], 0, parentGlobal, ED_MSG_TERM_ACK, sendcountv, MatrixCausal);
			
	ackv[parentGlobal]=0;
	parentGlobal=-1;
	}

	else
	if(sent_term==0){	
	sent_term=1;	
		  for (i = 0; i < neighc; i++) {

			#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d and I am sending END message %d of size %d to node %d with tag %d\n", rank, 				msg_end, 1, neighv[i], ED_MSG_TERM_END);
#endif
			
			ED_Send(&msg_end, 0, neighv[i], ED_MSG_TERM_END, sendcountv, MatrixCausal);
		    }

	}
		
		


	
	}   

	}

	    
	


if(rank==0){

printf("EXECUTION TIME(milliseconds): %ld\n", clock()-begin);
}
	
    ED_Finalize();

    MPI_Finalize();
    return 0;

}

int ED_Send(void *msg, int type_index, int dest, int tag_out, int *sendcountv, int MatrixCausal[][size])
{
    tag_out += type_index;

    if (type_msg_ord == 0) {

	//Node ordering in the messages
	MPI_Isend(msg, 1, tag_typev[type_index], dest, tag_out, MPI_COMM_WORLD, &request);

    } else 

   if (type_msg_ord == 1){

//fifo ordering
	int position, k1, k2, k;



	//k1 = ED_Calculate_buffer_pack_size(1, MPI_INT);
	MPI_Pack_size(1, MPI_INT, MPI_COMM_WORLD, &k1);
	//k2 = ED_Calculate_buffer_pack_size(msglen_send, tag_typev[type_index]);
	MPI_Pack_size(1, tag_typev[type_index], MPI_COMM_WORLD, &k2);

	k =  k1 + k2;
	char buff[k];


	position = 0;

	int seq;


	sendcountv[dest]++;

	seq = sendcountv[dest];

	//changing seq of messages in order to impose a fifo violation
/*if(rank==0){

	if (seq == 2) {
	    seq = 4;
	} else if (seq == 4) {
	    seq = 2;
	}
}*/
	//msgl = msglen_send;

	MPI_Pack(&seq, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);
	//ED_Add_item_to_pack(buff, k, &position, 1, &seq, MPI_INT);
	//ED_Add_item_to_pack(buff, k, &position, 1, &msgl, MPI_INT);
	//ED_Add_item_to_pack(buff, k, &position, msglen_send, msg, tag_typev[type_index]);

	//MPI_Pack(&msgl, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);

	MPI_Pack(msg, 1, tag_typev[type_index], buff, k, &position, MPI_COMM_WORLD);


	MPI_Send(buff, position, MPI_PACKED, dest, tag_out, MPI_COMM_WORLD);
/*
if(rank==0){
if (seq == 4) {
	    seq = 2;
	} else if (seq == 2) {
	    seq = 4;
	}
}*/


    }

   else 

   if (type_msg_ord == 2){

	//causal ordering
int k,k1,k2,position;

	



//changing seq of messages in order to impose a fifo violation

	


MatrixCausal[rank][dest]=MatrixCausal[rank][dest]+1;

/*if(rank==0){
if (MatrixCausal[rank][dest] == 2) {
	    MatrixCausal[rank][dest] = 4;
	} else if (MatrixCausal[rank][dest] == 4) {
	    MatrixCausal[rank][dest]  = 2;
	}

}*/

MPI_Pack_size(size*size, MPI_INT, MPI_COMM_WORLD, &k1);
MPI_Pack_size(1, tag_typev[type_index], MPI_COMM_WORLD, &k2);	

	

k =  k1 + k2;
	char buff[k];


	position = 0;

	
/*	for (i = 0; i < size; i++) {
	
	
	for(j=0;j<size;j++){
		
			printf("rank %d. causal %d %d %d\n", rank, i, j, MatrixCausal[i][j]);

		}

	
    }

*/	


	MPI_Pack(MatrixCausal, size*size, MPI_INT, buff, k, &position, MPI_COMM_WORLD);
	
	MPI_Pack(msg, 1, tag_typev[type_index], buff, k, &position, MPI_COMM_WORLD);


	MPI_Send(buff, position, MPI_PACKED, dest, tag_out, MPI_COMM_WORLD);


/*if(rank==0){
if (MatrixCausal[rank][dest] == 4) {
	    MatrixCausal[rank][dest] = 2;
	} else if (MatrixCausal[rank][dest] == 2) {
	    MatrixCausal[rank][dest]  = 4;
	}

}
*/

   }

else 

   if (type_msg_ord == 3){

	//causal relaxed ordering



	
   }



}

void *ED_Receive(int *sender, int *type_index, int *tag_in, int *recvcountv, int MatrixCausal[][size], Node heap[][ED_HEAP_SIZE], int *heap_count, int *heap_free)
{


    void *msg;
    int seq;
 


    if (type_msg_ord == 0) {

	//Node ordering in the messages

	MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);


	*sender = status.MPI_SOURCE;

	*tag_in = status.MPI_TAG;
	
	
	

	if (*tag_in >= 100 && *tag_in < 200) {
	    //a model message
	    *type_index = *tag_in - 100;
	    *tag_in = 100;
	} else if (*tag_in >= 200 && *tag_in < 300) {
	    *type_index = *tag_in - 200;
	    *tag_in = 200;
	} else if (*tag_in >= 300 && *tag_in < 400) {
	    *type_index = *tag_in - 300;
	    *tag_in = 300;
	}
	//#ifndef NOT_USE_PRINTF printf("TAG %d! Index type %d\n", *tag_in, *type_index);



	//MPI_Get_elements(&status, tag_typev[*type_index], msglen_recv);

	ED_Msg_buff_allocate(size_elem_typev[*type_index], *sender, &msg);
           
	MPI_Recv(msg, 1, tag_typev[*type_index], status.MPI_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

    } else 

      if (type_msg_ord == 1){

	//fifo ordering
   


	fifo_out_order = 1;
	take_out_heap = 0;

	for (i = 0; i < size; i++) {

	    if (heap[i][0].seq == recvcountv[i] + 1 && i != rank) {
		//can take out element 0 from node i from heap
		take_out_heap = 1;
		*sender = i;
		recvcountv[*sender]++;


		ED_Heap_remove(heap[*sender], heap_count[*sender], sizeof(Node), ED_Compare_Node_fifo);

		heap_count[*sender]--;

		msg = heap[*sender][heap_count[*sender]].msg;
		*sender = heap[*sender][heap_count[*sender]].sender;
		*tag_in = heap[*sender][heap_count[*sender]].tag;
		seq = heap[*sender][heap_count[*sender]].seq;
		*type_index = heap[*sender][heap_count[*sender]].type_index;
		#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I'm taking message out of the HEAP from %d, whose seq is %d!\n", *sender, seq);
#endif


		break;



	    }

	}


	if (take_out_heap == 0) {


	    while (fifo_out_order == 1) {



		MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

		*sender = status.MPI_SOURCE;

		*tag_in = status.MPI_TAG;

		if (*tag_in >= 100 && *tag_in < 200) {
		    //a model message
		    *type_index = *tag_in - 100;
		    *tag_in = 100;
		} else if (*tag_in >= 200 && *tag_in < 300) {
		    *type_index = *tag_in - 200;
		    *tag_in = 200;
		} else if (*tag_in >= 300 && *tag_in < 400) {
		    *type_index = *tag_in - 300;
		    *tag_in = 300;
		}
		 
		

		MPI_Get_count(&status, MPI_PACKED, &buffsize);
		#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! get count packed message %d \n", rank, buffsize);
#endif



		char buff[buffsize];


		int position = 0;

		



		MPI_Recv(buff, buffsize, MPI_PACKED, status.MPI_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);


		//ED_Unpack_item_from_pack(buff, *msglen_recv, &position, 1, &seq, MPI_INT);
		//ED_Unpack_item_from_pack(buff, *msglen_recv, &position, 1, &msgl, MPI_INT);
		MPI_Unpack(buff,buffsize, &position, &seq, 1, MPI_INT, MPI_COMM_WORLD);
		#ifndef NOT_USE_PRINTF 
printf("MODEL MSG: fifo message => seq %d\n", seq);
#endif

		
	
	        ED_Msg_buff_allocate(size_elem_typev[*type_index], *sender, &msg);
        
		//msg = malloc(msgl * (size_elem_typev[*type_index]));
		//ED_Unpack_item_from_pack(buff, *msglen_recv, &position, msgl, msg, tag_typev[*type_index]);
		MPI_Unpack(buff, buffsize, &position, msg, 1, tag_typev[*type_index], MPI_COMM_WORLD);

		

		if (seq != recvcountv[*sender] + 1) {

		    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! Msg OUT OF ORDER: %d and last received was %d. I'll have to postpone it!\n", rank, seq, recvcountv[*sender]);
#endif




		    heap[*sender][heap_free[*sender]].msg = msg;
		    heap[*sender][heap_free[*sender]].sender = *sender;
		    heap[*sender][heap_free[*sender]].tag = *tag_in;
		    heap[*sender][heap_free[*sender]].seq = seq;
		    heap[*sender][heap_free[*sender]].type_index = *type_index;
		    heap_count[*sender]++;
		    ED_Heap_insert(heap, heap_free[*sender], heap_count[*sender] - 1, heap_count[*sender], sizeof(Node), ED_Compare_Node_fifo);
		    heap_free[*sender]++;



		} else {
		    recvcountv[*sender]++;
		    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: msg IN ORDER: number %d and last received was %d\n", seq, recvcountv[*sender]);
#endif

		    fifo_out_order = 0;

		}


	    }

	}



    }

else 

   if (type_msg_ord == 2){

	//causal ordering
	int MatrixCausalAux[size][size];
	fifo_out_order = 1;
	take_out_heap = 0;
	
	for (i = 0; i < size; i++) {
	if(i!=rank){
		enabled_to_recv=0;
		if(heap[i][0].MatrixCausal!=NULL){

		if(heap[i][0].MatrixCausal[i][rank]==MatrixCausal[i][rank] + 1){
				
		enabled_to_recv=1;
		 #ifndef NOT_USE_PRINTF 
		printf("MODEL MSG: I am %d! Verifying heap: MatrixCausalHeap[%d][%d]= %d == MatrixCausal[%d][%d]= %d + 1!\n", rank, i, rank, heap[i][0].MatrixCausal[i][rank], i, rank, MatrixCausal[i][rank]);
	#endif
		

		}
		}		
		if(enabled_to_recv == 1){
		for(k=0;k<size;k++){
		if(k!=i){
			if(MatrixCausal[k][rank] < heap[i][0].MatrixCausal[k][rank]){
			enabled_to_recv=0;
			break;

			}

		}	
		}

		}		

	
	    if (enabled_to_recv==1) {
		//can take out element 0 from node i from heap

		take_out_heap = 1;
		*sender = i;

		
		
		
		//recvcountv[*sender]++;


		ED_Heap_remove(heap[*sender], heap_count[*sender], sizeof(Node), ED_Compare_Node_causal);

		heap_count[*sender]--;

		#ifndef NOT_USE_PRINTF 
		printf("MODEL MSG: I'm %d and I'm taking message out of the HEAP from %d, whose number in matrix is %d!\n", rank, *sender ,heap[*sender][heap_count[*sender]].MatrixCausal[*sender][rank]);
		#endif


		*type_index = heap[*sender][heap_count[*sender]].type_index;
		msg = heap[*sender][heap_count[*sender]].msg;
		*sender = heap[*sender][heap_count[*sender]].sender;
		*tag_in = heap[*sender][heap_count[*sender]].tag;
	
		for(k=0;k<size;k++){
				for(j=0;j<size;j++){
					if(heap[*sender][heap_count[*sender]].MatrixCausal[k][j]>MatrixCausal[k][j]){
					MatrixCausal[k][j]=heap[*sender][heap_count[*sender]].MatrixCausal[k][j];
					}	
				}

			}




		//seq = heap[*sender][heap_count[*sender]].seq;
		


		break;



	    }

	}
	}


	if (take_out_heap == 0) {


	    while (fifo_out_order == 1) {



		MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

		*sender = status.MPI_SOURCE;

		*tag_in = status.MPI_TAG;

		if (*tag_in >= 100 && *tag_in < 200) {
		    //a model message
		    *type_index = *tag_in - 100;
		    *tag_in = 100;
		} else if (*tag_in >= 200 && *tag_in < 300) {
		    *type_index = *tag_in - 200;
		    *tag_in = 200;
		} else if (*tag_in >= 300 && *tag_in < 400) {
		    *type_index = *tag_in - 300;
		    *tag_in = 300;
		}
		 
		

		MPI_Get_count(&status, MPI_PACKED, &buffsize);
		#ifndef NOT_USE_PRINTF 
		printf("MODEL MSG: I am %d and get count packed message %d \n", rank, buffsize);
		#endif



		char buff[buffsize];


		int position = 0;

		



		MPI_Recv(buff, buffsize, MPI_PACKED, status.MPI_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);


	
		MPI_Unpack(buff,buffsize, &position, MatrixCausalAux, size*size, MPI_INT, MPI_COMM_WORLD);


		/*for (i = 0; i < size; i++) {
	
	
		for(j=0;j<size;j++){
		
			printf("rank %d. causal aux %d %d %d\n", rank, i, j, MatrixCausalAux[i][j]);
	
			}

	
  		  }*/
	
	        ED_Msg_buff_allocate(size_elem_typev[*type_index], *sender, &msg);
        
		//msg = malloc(msgl * (size_elem_typev[*type_index]));
		//ED_Unpack_item_from_pack(buff, *msglen_recv, &position, msgl, msg, tag_typev[*type_index]);
		MPI_Unpack(buff, buffsize, &position, msg, 1, tag_typev[*type_index], MPI_COMM_WORLD);
		


		enabled_to_recv=0;
		if(MatrixCausalAux[*sender][rank]==MatrixCausal[*sender][rank] + 1){
		enabled_to_recv=1;

		}
		if(enabled_to_recv == 1){
		for(k=0;k<size;k++){
		if(k!=*sender){
			if(MatrixCausal[k][rank] < MatrixCausalAux[k][rank]){
				

			
			enabled_to_recv=0;
			break;

			}

		}	
		}

		}		



		
		if (enabled_to_recv==0) {

		    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! Msg OUT OF ORDER: MatrixCausal[%d][%d]=%d and MatrixCausalRecv[%d][%d]=%d. I'll have to postpone it!\n", rank, *sender, rank, MatrixCausal[*sender][rank], *sender, rank, MatrixCausalAux[*sender][rank]);
#endif



	
		   


			matrixIsNull=0;
			if(heap[*sender][heap_free[*sender]].MatrixCausal==NULL){
				
		    	matrixIsNull=1;	
			heap[*sender][heap_free[*sender]].MatrixCausal=malloc(size*sizeof(int*));
		
			}
			
		    
			for(i=0;i<size;i++){
				if(matrixIsNull==1){
				heap[*sender][heap_free[*sender]].MatrixCausal[i]=malloc(size*sizeof(int));	
				}
				for(j=0;j<size;j++){
				heap[*sender][heap_free[*sender]].MatrixCausal[i][j] = MatrixCausalAux[i][j];			

				}

			}	

				    
                    heap[*sender][heap_free[*sender]].msg = msg;
		    heap[*sender][heap_free[*sender]].sender = *sender;
		    heap[*sender][heap_free[*sender]].tag = *tag_in;			
		    heap[*sender][heap_free[*sender]].type_index = *type_index;
		    heap_count[*sender]++;
		    ED_Heap_insert(heap, heap_free[*sender], heap_count[*sender] - 1, heap_count[*sender], sizeof(Node), ED_Compare_Node_causal);
		    heap_free[*sender]++;



		} else {
		     #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! Msg IN ORDER: MatrixCausal[%d][%d]=%d and MatrixCausalRecv[%d][%d]=%d. I'll receive it!\n", rank, *sender, rank, MatrixCausal[*sender][rank], *sender, rank, MatrixCausalAux[*sender][rank]);
#endif

			for(i=0;i<size;i++){
				for(j=0;j<size;j++){
					if(MatrixCausalAux[i][j]>MatrixCausal[i][j]){
					MatrixCausal[i][j]=MatrixCausalAux[i][j];
					}	
				}

			}


		   // #ifndef NOT_USE_PRINTF 
//printf("MODEL MSG: msg IN ORDER: number %d and last received was %d\n", seq, recvcountv[*sender]);
//#endif

		    fifo_out_order = 0;

		}


	    }

	}







   }

else 

   if (type_msg_ord == 3){

	//causal relaxed ordering
   }



    return msg;


}
int ED_Compare_Node_fifo(const void *a, const void *b)
{
    return ((*(Node *) b).seq - (*(Node *) a).seq);
}
int ED_Compare_Node_causal(const void *a, const void *b)
{
    return ((*(Node *) b).MatrixCausal[(*(Node *) b).sender][rank] - (*(Node *) a).MatrixCausal[(*(Node *) a).sender][rank]);
}
